/**
 * FleetRoute.java
 *
 * Roslan's Remake of MOO in 3D using Ardor3D
 * (C) 2011 Roslan Amir. All rights reserved.
 */

package com.roslan.games.moo3d.spatial;

import java.awt.Point;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ardor3d.math.ColorRGBA;
import com.ardor3d.math.MathUtils;
import com.ardor3d.math.Matrix3;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.type.ReadOnlyColorRGBA;
import com.ardor3d.renderer.state.MaterialState;
import com.ardor3d.renderer.state.RenderState.StateType;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.Spatial;
import com.ardor3d.scenegraph.shape.Arrow;
import com.roslan.games.moo3d.Base;
import com.roslan.games.moo3d.utils.HexGridUtils;

/**
 * This class implements the route from from the current Fleet to the destination Star.
 * 
 * @author Roslan Amir
 * @version 1.0 - Jan 18, 2011
 */
public class FleetRoute extends Node {

	/**
     * 
     */
	private static final Logger logger = LoggerFactory.getLogger(Base.class);

	/**
	 * 
	 */
	private static final ReadOnlyColorRGBA IN_RANGE_COLOR = ColorRGBA.GREEN;

	/**
	 * 
	 */
	private static final ReadOnlyColorRGBA OUT_OF_RANGE_COLOR = ColorRGBA.RED;

	/**
	 * The POOL of discarded <code>Arrow</code> instances to be reused.
	 */
	private static final Deque<Arrow> POOL = new ArrayDeque<Arrow>();

	/**
	 * The root node of the scene graph where we attached this <code>FleetRoute</code>.
	 */
	private final Node root;

	/**
	 * The base used as the source for the other Arrows.
	 */
//	private final Arrow baseArrow = new Arrow("Arrow", HexGridUtils.HEXGRID_S, 0.05);
	private final Arrow baseArrow = new Arrow("Arrow", 0.5, 0.05);

	/**
	 * 
	 */
	private transient Point currentSourceTile;

	/**
	 * 
	 */
	private transient Point currentTargetTile;

	/**
	 * 
	 */
	private transient boolean currentInRange;

	/**
	 * Constructs a new <code>FleetRoute</code> with the given name.
	 * 
	 * @param name - the name of the node. This is required for identification purposes.
	 * @param root - the root node
	 */
	public FleetRoute(final String name, final Node root) {
		super(name);
		this.root = root;
	}

	/**
	 * Show the route from the current fleet location to the destination location.
	 *
	 * @param fleetNode - the fleet whose route is to show
	 * @param starNode - the star which is the destination
	 * @param inRange - true if the destination is in range, false otherwise
	 */
	public void show(final FleetNode fleetNode, final StarNode starNode, final boolean inRange) {
		/*
		 * We are actually redisplaying the current path, so just reattached this node to the root.
		 */
		if (fleetNode.tile.equals(currentSourceTile) && starNode.tile.equals(currentTargetTile)) {
			logger.info("FleetRoute.showFleetRoute() -- same source and target -- redisplay");
			if (inRange != currentInRange) {
				logger.info("FleetRoute.showFleetRoute() -- different color");
				MaterialState renderState = (MaterialState) this.getLocalRenderState(StateType.Material);
				renderState.setAmbient((inRange) ? IN_RANGE_COLOR : OUT_OF_RANGE_COLOR);
				currentInRange = inRange;
			}
		}

		/*
		 * We are displaying a new path, so dispose the previous spatials and make new ones.
		 */
		else {
			logger.info("FleetRoute.showFleetRoute() -- new path -- recreate");
			discardOldPathArrow();
//			discardOldPathLine();
			currentSourceTile = fleetNode.tile;
			currentTargetTile = starNode.tile;
			currentInRange = inRange;
			makeNewPathArrow(currentSourceTile, currentTargetTile, currentInRange);
//			makeNewPathLine(currentSourceTile, currentTargetTile, currentInRange);
		}

		root.attachChild(this);
	}

	/**
	 * Discard the previous path. This may involve putting back the spatials into the POOL.
	 */
	private void discardOldPathArrow() {
		List<Spatial> list = this.getChildren();
		for (Spatial spatial : list) {
			POOL.push((Arrow) spatial);
		}
		this.detachAllChildren();
	}

//	/**
//	 * Discard the previous path. This may involve putting back the spatials into the POOL.
//	 */
//	private void discardOldPathLine() {
//		this.detachAllChildren();
//	}

	/**
	 * Regenerate to show the path between the starting hex coordinates to the ending hex coordinates.
	 *
	 * @param sourceTile - the starting tile coordinates
	 * @param targetTile - the ending hex tile coordinates
	 * @param inRange - flag to indicate whether the path should display as IN_RANGE or OUT_OF_RANGE
	 */
	private void makeNewPathArrow(final Point sourceTile, final Point targetTile, final boolean inRange) {
		MaterialState materialState = (MaterialState) this.getLocalRenderState(StateType.Material);
		if (materialState == null) {
			materialState = new MaterialState();
			this.setRenderState(materialState);
		}
		materialState.setAmbient((inRange) ? IN_RANGE_COLOR : OUT_OF_RANGE_COLOR);

		final Matrix3 rotation = Matrix3.fetchTempInstance();
		final Vector3 translation = Vector3.fetchTempInstance();

		final Point[] tiles = HexGridUtils.path(sourceTile, targetTile);
		Point source = sourceTile;
		double[] origin = new double[2];
		for (Point target : tiles) {
			double angle;
			HexGridUtils.getHexWorldOrigin(source, origin);
			translation.set(origin[0], origin[1], 0.0);
			if (target.y > source.y) {
				if (target.x == source.x) { // upper-left
					angle = 30.0;
					translation.addLocal(-HexGridUtils.HEXGRID_R / 2, (HexGridUtils.HEXGRID_S + HexGridUtils.HEXGRID_H) / 2, 0);
				} else { // left
					angle = 90.0;
					translation.addLocal(-HexGridUtils.HEXGRID_R, 0, 0);
				}
			} else if (target.y == source.y) {
				if (target.x > source.x) { // upper-right
					angle = -30.0;
					translation.addLocal(HexGridUtils.HEXGRID_R / 2, (HexGridUtils.HEXGRID_S + HexGridUtils.HEXGRID_H) / 2, 0);
				} else { // lower-left
					angle = 150.0;
					translation.addLocal(-HexGridUtils.HEXGRID_R / 2, -(HexGridUtils.HEXGRID_S + HexGridUtils.HEXGRID_H) / 2, 0);
				}
			} else { // target.y < source.y
				if (target.x > source.x) { // right
					angle = -90.0;
					translation.addLocal(HexGridUtils.HEXGRID_R, 0, 0);
				} else { // lower-right
					angle = -150.0;
					translation.addLocal(HexGridUtils.HEXGRID_R / 2, -(HexGridUtils.HEXGRID_S + HexGridUtils.HEXGRID_H) / 2, 0);
				}
			}
			Arrow arrow = (POOL.isEmpty()) ? (Arrow) baseArrow.makeCopy(true) : POOL.pop();
			arrow.setTranslation(translation);
			rotation.fromAngleNormalAxis(angle * MathUtils.DEG_TO_RAD, Vector3.UNIT_Z);
			arrow.setRotation(rotation);
			this.attachChild(arrow);
			source = target;
		}

		Matrix3.releaseTempInstance(rotation);
		Vector3.releaseTempInstance(translation);
	}

//	/**
//	 * Regenerate to show the path between the starting hex coordinates to the ending hex coordinates.
//	 *
//	 * @param from - the starting tile coordinates
//	 * @param to - the ending hex tile coordinates
//	 * @param inRange - flag to indicate whether the path should display as IN_RANGE or OUT_OF_RANGE
//	 */
//	private void makeNewPathLine(Point from, Point to, boolean inRange) {
//		MaterialState materialState = (MaterialState) this.getLocalRenderState(StateType.Material);
//		if (materialState == null) {
//			materialState = new MaterialState();
//			this.setRenderState(materialState);
//		}
//		materialState.setAmbient((inRange) ? IN_RANGE_COLOR : OUT_OF_RANGE_COLOR);
//
//		/*
//		 * Get the list of hex coordinates for the path.
//		 */
//		final Point[] tiles = HexGridUtils.path(from, to);
//
//		/*
//		 * Create the vertices for the line segments.
//		 */
//		final Vector3[] vertices = new Vector3[tiles.length * 2];
//		final double[] center = new double[2];
//		int vertexIndex = 0;
//
//		HexGridUtils.getHexWorldOrigin(from, center);
//		Vector3 vector = Vector3.fetchTempInstance();
//		vector.set(center[0], center[1], 0);
//		vertices[vertexIndex++] = vector;
//
//		int hexIndex = 0;
//		Point point;
//		for (int i = 0; i < tiles.length - 1; i++) {
//			point = tiles[hexIndex++];
//			HexGridUtils.getHexWorldOrigin(point, center);
//			vector = Vector3.fetchTempInstance();
//			vector.set(center[0], center[1], 0);
//			vertices[vertexIndex++] = vector;
//			vertices[vertexIndex++] = vector;
//		}
//		point = tiles[hexIndex++];
//		HexGridUtils.getHexWorldOrigin(point, center);
//		vector = Vector3.fetchTempInstance();
//		vector.set(center[0], center[1], 0);
//		vertices[vertexIndex] = vector;
//
//		/*
//		 * Create the Line object.
//		 */
//		final Line line = new Line("PathLine", vertices, null, null, null);
//		line.setLineWidth(3);
//		line.setStipplePattern((short) 0xF0F0);
//		line.updateModelBound();
//		this.attachChild(line);
//
//		/*
//		 * Return the Vector3 objects to the POOL
//		 */
//		for (Vector3 v : vertices) {
//			Vector3.releaseTempInstance(v);
//		}
//	}

	/**
	 * Hide the currently displayed route from the current fleet to the destination star.
	 */
	public void hide() {
		root.detachChild(this);
	}

}
